package com.lagou.edu.support;/**
 * import	java.security.Provider.Service;
 * import	java.util.List;
 * import	java.util.concurrent.ConcurrentHashMap;
 * Created by IntelliJ IDEA.
 * User: Yangdx.
 * Date: 2020/5/7.
 * Time: 18:55.
 * Tags: Code, we are serious.
 */

import com.alibaba.druid.util.StringUtils;
import com.lagou.edu.stereotype.Autowired;
import com.lagou.edu.stereotype.Service;
import com.lagou.edu.stereotype.Transactional;
import com.lagou.edu.utils.ClassUtils;
import com.lagou.edu.utils.TransactionManager;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 使用反射扫描包，同时根据自定义注解加载bean
 *
 * @author
 * @create 2020-05-07 18:55
 **/
public class ClassPathXmlApplicationContext {
    /**
     * 包的路径
     */
    private final String packagePath;
    /**
     * 存放注解对象的容器
     */
    private static ConcurrentHashMap<String, Object> beansMaps;

    public ClassPathXmlApplicationContext(String packagePath) throws Exception {
        this.packagePath = packagePath;
        beansMaps = new ConcurrentHashMap<>();
        initBeans();
        initResources();
        TransactionManager transactionManager = (TransactionManager)beansMaps.get("transactionManager");
        if(transactionManager!=null){
            beansMaps.keySet().forEach(
                    key->{
                        Class<?> clazz = beansMaps.get(key).getClass();
                        Method[] declaredMethods = clazz.getDeclaredMethods();
                        for (Method declaredMethod : declaredMethods) {
                            Transactional transaction = declaredMethod.getDeclaredAnnotation(Transactional.class);
                            if(transaction!=null){
                                TransactionEnabledAnnotationProxyManager transactionEnabledProxyManager = new TransactionEnabledAnnotationProxyManager(transactionManager);
                                Object proxy = transactionEnabledProxyManager.proxyFor(beansMaps.get(key));
                                beansMaps.put(key,proxy);
                                break;
                            }
                        }
                    }
            );
        }
    }

    /**
     * 初始化容器
     */
    private void initBeans() throws Exception {
        //使用反射获取包下的所有类
        List<Class<?>> classes = ClassUtils.getClasses(packagePath);
        ConcurrentHashMap<String, Object> beansMaps = findClassExistAnnotation(classes);
        if (beansMaps == null || beansMaps.isEmpty()) {
            throw new Exception("该包下没有注解的类");
        }
    }

    private void initResources() throws Exception {
        //获取并遍历所有注解对象
        Set<Map.Entry<String, Object>> entrySet = beansMaps.entrySet();
        for (Map.Entry<String, Object> entry : entrySet) {
            Object value = entry.getValue();
            attriAssign(value);
        }
    }

    /**
     * 依赖注入
     *
     * @param list
     * @return
     * @throws Exception
     */
    private void attriAssign(Object value) throws Exception {
        //用发射的方法获取当前类的所有属性
        Class<? extends Object> classes = value.getClass();
        Field[] fields = classes.getDeclaredFields();
        for (Field field : fields) {
            Autowired autowired = field.getDeclaredAnnotation(Autowired.class);
            if (autowired != null) {
                //获取属性名称
                String name = field.getName();
                Object bean = getBean(name);
                field.setAccessible(true);
                //给属性赋值
                field.set(value,bean);
            }
        }
    }

    public Object getBean(String beanId) throws Exception{
        if (StringUtils.isEmpty(beanId)) {
            throw new Exception("当前bean的ID不存在");
        }
        //从spring的bean容器中获取
        return beansMaps.get(beanId);
    }

    private ConcurrentHashMap<String, Object> findClassExistAnnotation(List<Class<?>> list) throws Exception {
        for (Class<?> clazz : list) {
            Service service = clazz.getDeclaredAnnotation(Service.class);
            if (service != null) {
                //拿到当前类名
                String className = clazz.getSimpleName();
                Object bean = clazz.newInstance();
                //拿到当前类实现的所有接口
                Class<?>[] interfaces = clazz.getInterfaces();
                if(!com.mysql.jdbc.StringUtils.isNullOrEmpty(service.value())){
                    //拿到小写类名
                    String beanId = toLowerCaseFirstOne(service.value());
                    //将所有接口注入为bean
                    beansMaps.put(beanId, bean);
                }else {
                    if(interfaces.length>0){
                        for (Class<?> anInterface : interfaces) {
                            //拿到小写类名
                            String beanId = toLowerCaseFirstOne(anInterface.getSimpleName());
                            //将所有接口注入为bean
                            beansMaps.put(beanId, bean);
                        }
                    }else {
                        //拿到小写类名
                        String beanId = toLowerCaseFirstOne(className);
                        //将所有接口注入为bean
                        beansMaps.put(beanId, bean);
                    }
                }
            }
        }
        return beansMaps;
    }

    /**
     * 首字母转小写
     *
     * @param className
     * @return String
     */
    private static String toLowerCaseFirstOne(String className) {
        if (Character.isLowerCase(className.charAt(0))) {
            return className;
        }
        return String.format("%s%s", Character.toLowerCase(className.charAt(0)),
                className.substring(1));
    }
}
